package classifiers;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import mulan.classifier.InvalidDataException;
import mulan.classifier.ModelInitializationException;
import mulan.classifier.MultiLabelLearner;
import mulan.classifier.MultiLabelOutput;
import mulan.data.MultiLabelInstances;
import utils.TagCluster;
import utils.TagClustering;
import weka.core.Instance;

public class TwoLevelClassifier implements MultiLabelLearner {
	
	private MultiLabelLearner level_one_classifier;
	private HashMap<Integer,MultiLabelLearner>  level_two_classifiers;
	private int level_one_choose_best;
	private double level_one_threshold;
	private boolean to_multiply;
	private TagClustering tc;
	
	public TwoLevelClassifier() {}
	
	public void setLevelTwoClassifierForCluster(Integer cluster_idx,MultiLabelLearner learner) {
		level_two_classifiers.put(cluster_idx, learner);
	}
	
	public MultiLabelLearner getLevelTwoClassifierForCluster(Integer cluster_idx) {
		return level_two_classifiers.get(cluster_idx);
	}

	public MultiLabelLearner getLevel_one_classifier() {
		return level_one_classifier;
	}

	public void setLevel_one_classifier(MultiLabelLearner level_one_classifier) {
		this.level_one_classifier = level_one_classifier;
	}

	public int getLevel_one_choose_best() {
		return level_one_choose_best;
	}

	public void setLevel_one_choose_best(int level_one_choose_best) {
		this.level_one_choose_best = level_one_choose_best;
	}

	public double getLevel_one_threshold() {
		return level_one_threshold;
	}

	public void setLevel_one_threshold(double level_one_threshold) {
		this.level_one_threshold = level_one_threshold;
	}

	public boolean isTo_multiply() {
		return to_multiply;
	}

	public void setTo_multiply(boolean to_multiply) {
		this.to_multiply = to_multiply;
	}

	public TagClustering getTc() {
		return tc;
	}

	public void setTc(TagClustering tc) {
		this.tc = tc;
	}

	public TwoLevelClassifier(TagClustering tc) {
		this(5,.0,true,tc);
	}

	public TwoLevelClassifier(int level_one_choose_best,
			double level_one_threshold, boolean to_multiply, TagClustering tc) {
		this.level_one_choose_best = level_one_choose_best;
		this.level_one_threshold = level_one_threshold;
		this.to_multiply = to_multiply;
		this.tc = tc;
	}

	@Override
	public void build(MultiLabelInstances arg0) throws Exception,
			InvalidDataException {
		System.err.println("THIS IS NOT A BUILDABLE CLASSIFIER");
		System.err.println("YOU NEED TO SET THE ALREADY BUILT BASE CLASSIFIERS");
	}

	@Override
	public boolean isUpdatable() {
		return false;
	}

	@Override
	public MultiLabelLearner makeCopy() throws Exception {
		return this;
	}

	@Override
	public MultiLabelOutput makePrediction(Instance instance) throws Exception,
			InvalidDataException, ModelInitializationException {
		//make level one predictions
		MultiLabelOutput mlo_level_one = level_one_classifier.makePrediction(instance);
		//determine the best clusters
		double confidences[] = Arrays.copyOf(mlo_level_one.getConfidences(), mlo_level_one.getConfidences().length);
		Arrays.sort(confidences);
		double threshold = 0.0;
		for ( int k = confidences.length-1 ; k > confidences.length-level_one_choose_best-1 ; --k ) {
			if ( confidences[k] < level_one_threshold ) break;
			threshold = confidences[k];
		}
		// make prediction locally for each good cluster
		List<PredictionForCluster> predictions = new LinkedList<PredictionForCluster>();
		for ( int i = 0 ; i < mlo_level_one.getConfidences().length ; ++i ) {
			if ( mlo_level_one.getConfidences()[i] > threshold ) {
				predictions.add(new PredictionForCluster(i, to_multiply?mlo_level_one.getConfidences()[i]:1.0,level_two_classifiers.get(i).makePrediction(instance)));
			}
		}
		//combine the predictions from local clusters to a single prediction in an orderly manner
		double probabilities[] = new double[tc.getNumTags()];
		for ( PredictionForCluster pfc : predictions ) {
			int cluster_idx = pfc.getCluster_idx();
			TagCluster cluster = tc.getAllClusters().get(cluster_idx);
			int i = 0;
			for ( String tag : cluster.getTags() ) {
				probabilities[tc.getTagIdx(tag)] = pfc.getConfidence(i++);
			}
		}
		return new MultiLabelOutput(probabilities);
	}

	@Override
	public void setDebug(boolean arg0) {
		
	}
		
}
